package cn.virens.common.util.core.number;

import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
import java.util.Optional;

/**
 * BigDecimal 工具包 2017年10月13日 下午1:58:04
 *
 * @author virens
 */
public class BigDecimalUtil {
    public static final MathContext DEFAULT_MC = new MathContext(4, RoundingMode.HALF_UP);

    /**
     * BigDecimal比较： A &lt; B
     *
     * @param a Number A
     * @param b Number B
     * @return Boolean
     */
    public static boolean less(BigDecimal a, BigDecimal b) {
        if (a == null) return false;
        if (b == null) return false;

        return a.compareTo(b) == -1;
    }

    /**
     * BigDecimal比较： A &lt;= B
     *
     * @param a Number A
     * @param b Number B
     * @return Boolean
     */
    public static boolean lessAndEqual(BigDecimal a, BigDecimal b) {
        if (a == null) return false;
        if (b == null) return false;

        int compare = a.compareTo(b);

        return compare == -1 || compare == 0;
    }

    /**
     * BigDecimal比较： A = B
     *
     * @param a Number A
     * @param b Number B
     * @return Boolean
     */
    public static boolean equal(BigDecimal a, BigDecimal b) {
        if (a == null) return false;
        if (b == null) return false;

        return a.compareTo(b) == 0;
    }

    /**
     * BigDecimal比较： A &gt;= B
     *
     * @param a Number A
     * @param b Number B
     * @return Boolean
     */
    public static boolean greaterAndEqual(BigDecimal a, BigDecimal b) {
        if (a == null) return false;
        if (b == null) return false;

        int compare = a.compareTo(b);

        return compare == 0 || compare == 1;
    }

    /**
     * BigDecimal比较： A &gt; B
     *
     * @param a Number A
     * @param b Number B
     * @return Boolean
     */
    public static boolean greater(BigDecimal a, BigDecimal b) {
        if (a == null) return false;
        if (b == null) return false;

        return a.compareTo(b) == 1;
    }

    /**
     * BigDecimal基本运算: a - b
     *
     * @param a Number A
     * @param b Number B
     * @return BigDecimal
     */
    public static BigDecimal sub(BigDecimal a, BigDecimal b) {
        return zero(Optional.ofNullable(a).map(_a -> {
            return Optional.ofNullable(b).map(_b -> {
                return _a.subtract(_b);
            }).orElse(_a);
        }));
    }

    /**
     * BigDecimal基本运算: a + b
     *
     * @param a Number A
     * @param b Number B
     * @return BigDecimal
     */
    public static BigDecimal add(BigDecimal a, BigDecimal b) {
        return zero(Optional.ofNullable(a).map(_a -> {
            return Optional.ofNullable(b).map(_b -> {
                return _a.add(_b);
            }).orElse(_a);
        }));
    }

    /**
     * BigDecimal基本运算: a * b
     *
     * @param a Number A
     * @param b Number B
     * @return BigDecimal
     */
    public static BigDecimal mul(BigDecimal a, BigDecimal b) {
        return zero(Optional.ofNullable(a).map(_a -> {
            return zero(Optional.ofNullable(b).map(_b -> {
                return _a.multiply(_b);
            }));
        }));
    }

    /**
     * BigDecimal基本运算: a / b
     *
     * @param a Number A
     * @param b Number B
     * @return BigDecimal
     */
    public static BigDecimal div(BigDecimal a, BigDecimal b) {
        return zero(Optional.ofNullable(a).map(_a -> {
            return zero(Optional.ofNullable(b).map(_b -> {
                return _a.divide(_b, DEFAULT_MC);
            }));
        }));

    }

    /**
     * BigDecimal运算: 求和
     *
     * @param bs 需要求和的BigDecimal
     * @return BigDecimal
     */
    public static BigDecimal sum(BigDecimal... bs) {
        BigDecimal result = BigDecimal.ZERO;

        if (bs != null) {
            for (BigDecimal b : bs) {
                result = add(result, b);
            }
        }

        return result;
    }

    /**
     * BigDecimal转换：String -&gt; BigDecimal,defaultValue:null
     *
     * @param val String
     * @return BigDecimal
     */
    public static BigDecimal valueOf(String val) {
        return valueOf(val, null);
    }

    /**
     * BigDecimal转换：Integer -&gt; BigDecimal,defaultValue:null
     *
     * @param val Integer
     * @return BigDecimal
     */
    public static BigDecimal valueOf(Integer val) {
        return valueOf(val, null);
    }

    /**
     * BigDecimal转换：Long -&gt; BigDecimal,defaultValue:null
     *
     * @param val Long
     * @return BigDecimal
     */
    public static BigDecimal valueOf(Long val) {
        return valueOf(val, null);
    }

    /**
     * BigDecimal转换：Float -&gt; BigDecimal,defaultValue:null
     *
     * @param val Float
     * @return BigDecimal
     */
    public static BigDecimal valueOf(Float val) {
        return valueOf(val, null);
    }

    /**
     * BigDecimal转换：Double -&gt; BigDecimal,defaultValue:null
     *
     * @param val Double
     * @return BigDecimal
     */
    public static BigDecimal valueOf(Double val) {
        return valueOf(val, null);
    }

    /**
     * 从Object 转为 BigDecimal
     *
     * @param val Object
     * @return BigDecimal
     */
    public static BigDecimal valueOf(Object val) {
        if (val instanceof BigDecimal) {
            return (BigDecimal) val;
        } else if (val instanceof Number) {
            return new BigDecimal(((Number) val).doubleValue());
        } else if (val instanceof String) {
            return new BigDecimal((String) val);
        } else {
            return BigDecimal.ZERO;
        }
    }

    /**
     * BigDecimal转换：BigDecimal -&gt; BigDecimal,defaultValue:0
     *
     * @param val BigDecimal
     * @return BigDecimal
     */
    public static BigDecimal valueOfDefaultZreo(BigDecimal val) {
        return valueOf(val, BigDecimal.ZERO);
    }

    /**
     * BigDecimal转换：String -&gt; BigDecimal,defaultValue:0
     *
     * @param val String
     * @return BigDecimal
     */
    public static BigDecimal valueOfDefaultZreo(String val) {
        return valueOf(val, BigDecimal.ZERO);
    }

    /**
     * BigDecimal转换：Integer -&gt; BigDecimal,defaultValue:0
     *
     * @param val Integer
     * @return BigDecimal
     */
    public static BigDecimal valueOfDefaultZreo(Integer val) {
        return valueOf(val, BigDecimal.ZERO);
    }

    /**
     * BigDecimal转换：Long -&gt; BigDecimal,defaultValue:0
     *
     * @param val Long
     * @return BigDecimal
     */
    public static BigDecimal valueOfDefaultZreo(Long val) {
        return valueOf(val, BigDecimal.ZERO);
    }

    /**
     * BigDecimal转换：Float -&gt; BigDecimal,defaultValue:0
     *
     * @param val Float
     * @return BigDecimal
     */
    public static BigDecimal valueOfDefaultZreo(Float val) {
        return valueOf(val, BigDecimal.ZERO);
    }

    /**
     * BigDecimal转换：Double -&gt; BigDecimal,defaultValue:0
     *
     * @param val Double
     * @return BigDecimal
     */
    public static BigDecimal valueOfDefaultZreo(Double val) {
        return valueOf(val, BigDecimal.ZERO);
    }

    /**
     * BigDecimal转换：BigDecimal -&gt; BigDecimal
     *
     * @param val          BigDecimal
     * @param defaultValue default BigDecimal
     * @return BigDecimal
     */
    public static BigDecimal valueOf(BigDecimal val, BigDecimal defaultValue) {
        return val != null ? val : defaultValue;
    }

    /**
     * BigDecimal转换：String -&gt; BigDecimal
     *
     * @param val          String
     * @param defaultValue default BigDecimal
     * @return BigDecimal
     */
    public static BigDecimal valueOf(String val, BigDecimal defaultValue) {
        if (val == null || val.length() == 0) {
            return defaultValue;
        } else {
            return new BigDecimal(val);
        }
    }

    /**
     * BigDecimal转换：Integer -&gt; BigDecimal
     *
     * @param val          Integer
     * @param defaultValue default BigDecimal
     * @return BigDecimal
     */
    public static BigDecimal valueOf(Integer val, BigDecimal defaultValue) {
        if (val != null) {
            return new BigDecimal(Integer.toString(val));
        } else {
            return defaultValue;
        }
    }

    /**
     * BigDecimal转换：Long -&gt; BigDecimal
     *
     * @param val          Long
     * @param defaultValue default BigDecimal
     * @return BigDecimal
     */
    public static BigDecimal valueOf(Long val, BigDecimal defaultValue) {
        if (val != null) {
            return new BigDecimal(Long.toString(val));
        } else {
            return defaultValue;
        }
    }

    /**
     * BigDecimal转换：Float -&gt; BigDecimal
     *
     * @param val          Float
     * @param defaultValue default BigDecimal
     * @return BigDecimal
     */
    public static BigDecimal valueOf(Float val, BigDecimal defaultValue) {
        if (val != null) {
            return new BigDecimal(Float.toString(val));
        } else {
            return defaultValue;
        }
    }

    /**
     * BigDecimal转换：Double -&gt; BigDecimal
     *
     * @param val          Double
     * @param defaultValue default BigDecimal
     * @return BigDecimal
     */
    public static BigDecimal valueOf(Double val, BigDecimal defaultValue) {
        if (val != null) {
            return new BigDecimal(Double.toString(val));
        } else {
            return defaultValue;
        }
    }

    private static BigDecimal zero(Optional<BigDecimal> opt) {
        return opt.orElse(BigDecimal.ZERO);
    }
}
